#ifndef QP_QHSM_HPP
#define QP_QHSM_HPP

#include <memory>

#include "QEvt.hpp"

namespace QP {

using QState = std::uint_fast8_t;
using QStateHandler = QState (*)(void* const me,
                                 const std::shared_ptr<const QEvt>& e);

class QHsm {
 public:
  virtual ~QHsm() = default;
  virtual void init(void);
  virtual void init(const std::shared_ptr<const QEvt>& e);
  virtual void dispatch(const std::shared_ptr<const QEvt>& e);
  virtual bool isIn(QStateHandler const s);
  virtual QStateHandler state(void) const { return m_state; }

  static QState top(void* const me, const std::shared_ptr<const QEvt>& e);

 protected:
  QHsm(QStateHandler const initial);

  static QState const Q_RET_SUPER = static_cast<QState>(0);
  static QState const Q_RET_UNHANDLED = static_cast<QState>(2);
  static QState const Q_RET_HANDLED = static_cast<QState>(3);
  static QState const Q_RET_IGNORED = static_cast<QState>(4);
  static QState const Q_RET_ENTRY = static_cast<QState>(5);
  //! state exit  action executed
  static QState const Q_RET_EXIT = static_cast<QState>(6);
  //! return value without any effect
  static QState const Q_RET_NULL = static_cast<QState>(7);
  //! regular transition taken
  static QState const Q_RET_TRAN = static_cast<QState>(8);

  QState tran(QStateHandler const target) {
    m_temp = target;
    return Q_RET_TRAN;
  }

  QState super(QStateHandler const superstate) {
    m_temp = superstate;
    return Q_RET_SUPER;
  }
  enum ReservedHsmSignals {
    Q_ENTRY_SIG = 1,  //!< signal for entry actions
    Q_EXIT_SIG,       //!< signal for exit actions
    Q_INIT_SIG        //!< signal for nested initial transitions
  };

 private:
  enum {
    MAX_NEST_DEPTH_ = 6  //!< maximum nesting depth of states in HSM
  };
  //! internal helper function to take a transition in QP::QHsm
  int_fast8_t hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_]);

  friend class QActive;
  friend class QF;

 private:
  QStateHandler m_state;  ///< current active state (state-variable)
  QStateHandler m_temp;   ///< temporary state: target of tran. or superstate
};

//! Offset or the user signals
int const Q_USER_SIG = static_cast<int>(4);
}

//! Macro to specify that the event was handled
#define Q_HANDLED() (Q_RET_HANDLED)

//! Macro to specify that the event was NOT handled
//! due to a guard condition evaluating to 'false'
#define Q_UNHANDLED() (Q_RET_UNHANDLED)

//! Macro to specify a transition (also used for initial transitions).
/// @usage
/// @include qep_qtran.cpp
#define Q_TRAN(target_) (me->tran(Q_STATE_CAST(target_)))

//! Macro to specify the superstate of a given state.
/// @usage
/// @include qep_qhsm.cpp
#define Q_SUPER(state_) (me->super(Q_STATE_CAST(state_)))

//! Macro to perform casting to QStateHandler.
/// @description
/// This macro encapsulates the cast of a specific state handler function
/// pointer to QStateHandler, which violates MISRA-C 2004 rule 11.4(advisory).
/// This macro helps to localize this deviation.
#define Q_STATE_CAST(handler_) (reinterpret_cast<QP::QStateHandler>(handler_))

#endif  // QP_QHSM_HPP
